/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.cezary.butler.walili.client;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import pl.cezary.butler.walili.client.db.tasks.FetchDownloadTasks;
import pl.cezary.butler.walili.client.db.tasks.InitClientTasks;
import pl.cezary.butler.walili.client.db.tasks.UpdateDownloadTask;
import pl.cezary.butler.walili.client.mime.DownloadingMimeTypeAction;
import pl.cezary.butler.walili.server.db.Database;
import pl.cezary.butler.walili.server.db.tasks.FetchingTask;
import pl.cezary.butler.walili.server.db.utils.UnitInterruptedException;
import pl.cezary.butler.walili.server.shuffling.DownloadTaskShufflingComparator;
import pl.cezary.butler.walili.server.shuffling.ShufflingComparator;

/**
 *
 * @author Czarek
 */
public class DownloadWorker implements Runnable {
    private static final Logger log = Logger.getLogger(DownloadWorker.class.getName());
    private static final int tryFetchMoreLimit = 10;
    private static final int checkDelay = 7;
    private final Database db = (Database) DownloaderApp.getContext().getBean("client_db");
    private final ExecutorService executor = Executors.newFixedThreadPool(2);
    private final AtomicInteger counterDone = new AtomicInteger();
    private final AtomicInteger counterReadyToRun = new AtomicInteger();

    private List<DownloadTask> fetchMore(int no) {
        FetchingTask<List<DownloadTask>> task = new FetchDownloadTasks(no);
        db.executeDbTask(task);
        return task.getResults();
    }

    @Override
    public void run() {
        int counterQueued =0;
        final DownloadingMimeTypeAction action = new DownloadingMimeTypeAction();
        try {
            final Queue<DownloadTask> taskToDo = new PriorityQueue<DownloadTask> (30, new DownloadTaskShufflingComparator() );
            db.executeDbTask(new InitClientTasks());

            while (!UnitInterruptedException.isInterrupted()) {
                DownloadTask taskRef;
                while((taskRef = taskToDo.poll())!=null) {
                    final DownloadTask task = taskRef;
                    ++counterQueued;
                    task.setStatus(DownloadTaskStatus.QUEUED);
                    db.executeDbTask(new UpdateDownloadTask(task));
                    counterReadyToRun.incrementAndGet();
                    executor.submit(new Runnable() {

                        @Override
                        public void run() {                            
                            task.setStatus(DownloadTaskStatus.IN_PROGRES);
                            db.executeDbTask(new UpdateDownloadTask(task));
                            boolean result = false;
                            try {
                                result = action.process(task.getUrl().openConnection());
                            } catch (IOException ex) {
                                log.log(Level.SEVERE, null, ex);
                            }
                            task.setStatus(result ? DownloadTaskStatus.DONE : DownloadTaskStatus.FAILURE);
                            counterDone.incrementAndGet();
                            counterReadyToRun.decrementAndGet();
                            db.executeDbTask(new UpdateDownloadTask(task));
                        }
                    });
                }
                if (counterReadyToRun.get()<tryFetchMoreLimit) {
                    taskToDo.addAll(fetchMore(30));
                }else{
                    TimeUnit.SECONDS.sleep(checkDelay);
                }
            }

        } catch (InterruptedException ex) {            
            log.log(Level.FINE,"Download worker was interrupted",ex);
        } catch (UnitInterruptedException ex) {
            log.log(Level.FINE,"Download worker was interrupted",ex);
        } finally {            
            log.log(Level.INFO,"Downloading service is being shutdown. {0} tasks were queued and {1} done",new Object[]{counterQueued, counterDone});
            try {
                executor.shutdown();
                if (!executor.awaitTermination(500, TimeUnit.MILLISECONDS)) {
                    final List<Runnable> leftBehind = executor.shutdownNow();
                    log.log(Level.INFO, "{0} tasks have been left behind", leftBehind.size());
                }
                log.info("Downloading service has been shutdown");
            } catch (InterruptedException ex) {
                log.log(Level.SEVERE, "Waiting for downloading service termination was interrupted", ex);
            }
            
            db.rozlacz();
        }


    }
}
